using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

using System.Drawing;
using System.Drawing.Drawing2D;

using System.Windows.Forms;
using System.ComponentModel;
using Athena.Core;



namespace Athena.Objects
{
    public enum Corner
    {
        TopLeft,
        TopRight,
        BottomLeft,
        BottomRight,
        None,
        ControlPoint  //for the bezier points        
    }
    
    [Serializable]
    public abstract class IDrawable : ICloneable
    {

        public enum HorizontalAlignment
        {
            None,
            Left,
            Center,
            Right,
            LeftOfLeft,
            RightOfRight
        }
        public enum VerticalAlignment
        {
            None,
            Top,
            Middle,
            Bottom,
            AboveTop,
            BelowBottom
        }

        /// <summary>
        /// horizontal label position
        /// </summary>
        protected HorizontalAlignment _LabelH = HorizontalAlignment.None;
        [CategoryAttribute("IDrawable Attributes"), Description("horizontal position of the label")]
        public HorizontalAlignment TextH
        {
            get { return _LabelH; }
            set { _LabelH = value; }
        }

        /// <summary>
        /// the vertical label position
        /// </summary>
        protected VerticalAlignment _LabelV = VerticalAlignment.None;
        [CategoryAttribute("IDrawable Attributes"), Description("vertical position of the label")]        
        public VerticalAlignment TextV
        {
            get { return _LabelV; }
            set { _LabelV = value; }
        }
        protected Item _Item;
        
        /// <summary>
        /// the underlying model item represented by this drawable
        /// </summary>
        public virtual Item Item
        {
            get 
            {
                if (_Original != null && _Original != this)
                    return _Original.Item;
                return _Item; 
            }
            set 
            {
                if (value == null && _Item != null && _Item.Drawable == this)
                    _Item.Remove();

                _Item = value;
                if (value != null && value.Drawable != this &&
                    !(value.Drawable != null && value.Drawable.Alias != null 
                        && value.Drawable.Alias.Contains(this)))
                {
                    value.Drawable = this;
                }
                //if (Parent != null)
                //    Parent.ChildChanged(this);
            }
        }

        /// <summary>
        /// the type of this item
        /// </summary>
        [CategoryAttribute("IDrawable Attributes"), Description("The type of this item in the model")]        
        public virtual string Type
        {
            get 
            {
                if (_Item != null)
                    return _Item.ItemType;
                else
                    return GetType().ToString();
            }
            set 
            {
                if (_Item != null)
                    _Item.ItemType = value; 
            }
        }

        /// <summary>
        /// sets the tool tip that describes this drawable
        /// </summary>
        /// <param name="tooltip">tool tip to use</param>
        public virtual void ShowToolTip(ToolTip tooltip, Control pnlcanvas, PointF location)
        {
            tooltip.ToolTipTitle = Type;
        }

        /// <summary>
        /// Tool tip for the corners of this item
        /// </summary>
        /// <param name="tooltip">the tool tip to use</param>
        /// <param name="pnlcanvas">the canvas for the tool tip</param>
        /// <param name="location">location of mouse</param>
        /// <param name="corner">corner</param>
        public virtual void ShowCornerToolTip(ToolTip tooltip, Control pnlcanvas, PointF location, Corner corner)
        {
            tooltip.SetToolTip(pnlcanvas, "change size");
        }

        protected Color _oFillColorStart, _oFillColorEnd;

        /// <summary>
        /// start color for gradient fills
        /// </summary>
        [System.ComponentModel.CategoryAttribute("IDrawable Attributes"), 
        System.ComponentModel.DescriptionAttribute("Start Color of the Object.")]
        public virtual Color ColorStart
        {
            get { return _oFillColorStart; }
            set { _oFillColorStart = value; }
        }

        /// <summary>
        /// end color for gradient fills
        /// </summary>
        [System.ComponentModel.CategoryAttribute("IDrawable Attributes"),
        System.ComponentModel.DescriptionAttribute("End Color of the Object.")]
        public virtual Color ColorEnd
        {
            get { return _oFillColorEnd; }
            set { _oFillColorEnd = value; }
        }

        /// <summary>
        /// The top left location of this drawable
        /// </summary>
        [System.ComponentModel.CategoryAttribute("IDrawable Attributes"),
        System.ComponentModel.DescriptionAttribute("Location of the Object"),
        ReadOnlyAttribute(false), TypeConverter(typeof(PointFConverter))]
        public abstract PointF Location { get; set; }
        
        /// <summary>
        /// the center location of this drawable
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public virtual System.Drawing.PointF Center
        {
            get
            {
                System.Drawing.PointF oCenter = Location;
                oCenter.X += Dimensions.Width / 2f;
                oCenter.Y += Dimensions.Height / 2f;
                return oCenter;
            }
        }
        /// <summary>
        /// The size of this drawable
        /// </summary>
        [System.ComponentModel.CategoryAttribute("IDrawable Attributes"),
        System.ComponentModel.DescriptionAttribute("Dimensions of the Object"),
        ReadOnlyAttribute(false)]
        public abstract System.Drawing.SizeF Dimensions { get; set; }

        /// <summary>
        /// the bounds of this drawable
        /// </summary>
        [System.ComponentModel.CategoryAttribute("IDrawable Attributes"),
        System.ComponentModel.DescriptionAttribute("Bounds of the Object"),
        ReadOnlyAttribute(false), TypeConverter(typeof(RectangleFConverter))]
        public abstract System.Drawing.RectangleF Bounds { get; set; }
        
        /// <summary>
        /// Select this drawable
        /// </summary>
        public abstract void Select();

        /// <summary>
        /// deselect this drawable
        /// </summary>
        public abstract void Deselect();

        /// <summary>
        /// is selected
        /// </summary>
        [System.ComponentModel.CategoryAttribute("IDrawable Attributes"),
        System.ComponentModel.DescriptionAttribute("Whether or not the Object is Selected"),
        ReadOnlyAttribute(false)]
        public abstract bool IsSelected
        {
            get;
            set;
        }

        protected bool _IsVisible = true;

        /// <summary>
        /// is visibile on the screen
        /// </summary>
        [CategoryAttribute("IDrawable Attributes"), Description("Whether or not to render this item on the screen")]        
        public virtual bool IsVisible
        {
            get { return _IsVisible; }
            set { _IsVisible = value; }
        }

        protected bool _ShowLabel = true;

        /// <summary>
        /// indicates whether of not to render the label for this item
        /// </summary>
        [CategoryAttribute("IDrawable Attributes"), Description("Whether or not to render this item's label on the screen")]
        public virtual bool ShowText
        {
            get { return _ShowLabel; }
            set { _ShowLabel = value; }
        }

        /// <summary>
        /// The drawing format to use for this drawable
        /// </summary>
        public enum ViewType { Block, Schematic };
        protected ViewType _ViewType = ViewType.Block;

        /// <summary>
        /// The current drawing format for this drawawble
        /// </summary>
        [CategoryAttribute("IDrawable Attributes"), Description("The rendering style for this item")]
        public virtual ViewType View
        {
            get { return _ViewType; }
            set 
            { 
                _ViewType = value;
            }
        }

        /// <summary>
        /// Test if this drawable intersects with the given rectangle
        /// </summary>
        /// <param name="rect">rectangle to test intersection with</param>
        /// <returns></returns>
        public virtual bool IntersectsWith(RectangleF rect)
        {
            RectangleF oTemp = Bounds;
            //oTemp.Inflate(5, 5);
            return oTemp.IntersectsWith(rect);

        }

        /// <summary>
        /// check if the drawable contains the point
        /// </summary>
        /// <param name="point">a point</param>
        /// <returns></returns>
        public virtual bool Contains(PointF point)
        {
            RectangleF oTemp = Bounds;
            //oTemp.Inflate(5, 5);
            return oTemp.Contains(point);
        }

        /// <summary>
        /// change the drawable via one of its corners
        /// </summary>
        /// <param name="oDirection">direction or change</param>
        /// <param name="oCorner">corner to change</param>
        public abstract void Transform(System.Drawing.PointF oDirection, ref Corner oCorner, bool CtrlKey);

        /// <summary>
        /// undo the last moving operation
        /// </summary>
        public virtual void UndoMove()
        {
        }

        /// <summary>
        /// Move to a new location. All the anchored items will be moved as well
        /// </summary>
        /// <param name="newPoint">new location</param>
        public virtual void MoveTo(PointF newPoint)
        {
            PointF offset = Utility.SubtractPoints(newPoint, Location);
            /*if (Parent != null)
            {
                Parent.ChildChanged(this);
            }*/
            List<IDrawable> linked = new List<IDrawable>();
            linked.AddRange(AnchoredItems);

            for (int i = 0; i < linked.Count; ++i)
            {
                if (linked[i].AnchoredItems != null && linked[i].AnchoredItems.Count > 0)
                {
                    foreach (IDrawable id in linked[i].AnchoredItems)
                    {
                        if (!linked.Contains(id) && id != this) 
                            linked.Add(id);
                    }
                }
            }

            foreach (IDrawable id in linked)
            {
                if (!id.IsSelected && (id.Parent == null || id.Parent == Parent || id.Parent == this))
                    id.Location = Utility.AddPoints(id.Location,offset);
            }

            Location = newPoint;
        }

        /// <summary>
        /// Shift location by a given amount -- similar to MoveTo. All anchored items will be moved.
        /// </summary>
        /// <param name="offset">direction of shift</param>
        public virtual void Translate(PointF offset)
        {
            Location = Utility.AddPoints(Location, offset);

            List<IDrawable> linked = new List<IDrawable>();
            linked.AddRange(AnchoredItems);

            for (int i = 0; i < linked.Count; ++i)
            {
                if (linked[i].AnchoredItems != null && linked[i].AnchoredItems.Count > 0)
                {
                    foreach (IDrawable id in linked[i].AnchoredItems)
                    {
                        if (!linked.Contains(id) && id != this)
                            linked.Add(id);
                    }
                }
            }

            foreach (IDrawable id in linked)
            {
                if (!id.IsSelected && (id.Parent == null || id.Parent == Parent || id.Parent == this))
                    id.Location = Utility.AddPoints(id.Location, offset);
            }
        }
        /// <summary>
        /// check which corner was selected
        /// </summary>
        /// <param name="oRect"></param>
        /// <returns></returns>
        public virtual Corner Where(System.Drawing.RectangleF oRect) { return Corner.None; }
        /// <summary>
        /// which corner was selected
        /// </summary>
        /// <param name="oPoint"></param>
        /// <returns></returns>
        public virtual Corner Where(System.Drawing.PointF oPoint) { return Corner.None; }

        /// <summary>
        /// Clean up the drawable
        /// </summary>
        /// <param name="oCorner">last corner that was used</param>
        public virtual void DoneModifying(Corner oCorner, bool modifyAchoredItems) 
        {
            if (Parent != null)
            {
                Parent.ChildChanged(this);
            }

            if (!modifyAchoredItems) return;

            List<IDrawable> linked = new List<IDrawable>();
            linked.AddRange(AnchoredItems);

            for (int i = 0; i < linked.Count; ++i)
            {
                if (linked[i].AnchoredItems != null && linked[i].AnchoredItems.Count > 0)
                {
                    foreach (IDrawable id in linked[i].AnchoredItems)
                    {
                        if (!linked.Contains(id) && id != this) 
                            linked.Add(id);
                    }
                }
            }

            foreach (IDrawable id in linked)
            {
                id.DoneModifying(oCorner, false);
            }
        }

        /// <summary>
        /// render the drawable
        /// </summary>
        /// <param name="g">graphics</param>
        public abstract void Draw(System.Drawing.Graphics g);

        protected List<IDrawable> _Alias = new List<IDrawable>();

        /// <summary>
        /// A list of aliases of this drawable
        /// </summary>
        [CategoryAttribute("IDrawable Attributes"), Description("Aliases of this drawable")]        
        public virtual List<IDrawable> Alias
        {
            get { return _Alias; }
        }
        
        protected IDrawable _Original = null;

        /// <summary>
        /// If this drawable is an alias, then this property returns the original drawable
        /// If not an alias, then returns null.
        /// </summary>
        [CategoryAttribute("IDrawable Attributes"), Description("If an alias, this is the original drawable")]        
        public virtual IDrawable Original
        {
            get { return _Original; }
            set
            {
                if (value != _Original)
                {
                    if (_Original != null && _Original.Alias != null)
                    {
                        _Original.Alias.Remove(this);
                    }
                    _Original = value;

                    if (_Original != null)
                        _Original.AddAlias(this);
                }
            }
        }
        /// <summary>
        /// checks if this drawable is an alias or if it has no model item
        /// </summary>
        [CategoryAttribute("IDrawable Attributes"), Description("Is this item empty")]        
        public virtual bool NullItem
        {
            get
            {
                return (Original != null && Original != this) ||
                        _Item == null || (Item.Drawable != this);
            }
        }

        /// <summary>
        /// make a new alias -- a null clone
        /// </summary>
        /// <returns></returns>
        public virtual IDrawable GenerateAlias()
        {
            IDrawable clone = Clone() as IDrawable;
            if (clone != null)
            {
                AddAlias(clone);

                //foreach (IDrawable id in LinkedDrawables)
                //{
                    //IDrawable newlink = id.GenerateAlias();
                    //clone.LinkTo(newlink);
                //}
            }
            return clone;
        }

        /// <summary>
        /// declare a drawable as an alias of this drawable
        /// </summary>
        /// <param name="drawable">the drawable that will become the alias</param>
        public virtual void AddAlias(IDrawable drawable)
        {
            if (drawable == null || drawable == this) return;

            if (NullItem && _Original != null)
            {
                _Original.AddAlias(drawable);
            }
            else
            {
                if (_Alias == null) _Alias = new List<IDrawable>();
                if (!_Alias.Contains(drawable))
                {
                    bool unique = true;
                    foreach (IDrawable alias in _Alias)
                    {
                        if (alias.Location == drawable.Location && alias.Parent == drawable.Parent)
                        {
                            unique = false;
                            break;
                        }
                    }
                    if (unique)
                        _Alias.Add(drawable);
                }
                drawable._Original = this;
                drawable.Item = null;
                //drawable.Parent = Parent;
            }
        }

        /// <summary>
        /// remove an alias
        /// </summary>
        /// <param name="drawable">the alias to remove</param>
        public virtual void RemoveAlias(IDrawable drawable)
        {
            _Alias.Remove(drawable);
            drawable.Remove();
        }
        /// <summary>
        /// remove all aliases
        /// </summary>
        public virtual void RemoveAllAlias()
        {
            List<IDrawable> aliases = new List<IDrawable>();
            aliases.AddRange(_Alias);
            foreach (IDrawable drawable in aliases) 
                drawable.Remove();
            _Alias.Clear();
        }
        /// <summary>
        /// Removes the anchored drawables and item. If there is an alias, the alias recieves the item
        /// </summary>
        public virtual void Remove() 
        { 
            Program.Model.RemoveDrawable(this);

            if (Alias != null && Alias.Count > 0)   //give the item to the alias
            {
                IDrawable heir = _Alias[0];       //get the first alias
                if (_Item != null)
                    _Item.Drawable = heir;     //make the alias into the original
                if (heir._Item != _Item)      //the alias does not want to become original
                    heir.Remove();           //in that case, remove it
                else
                {
                    _Alias.RemoveAt(0);           //else, make it the original
                    heir.Alias.AddRange(_Alias);
                    heir._Original = null;
                    if (heir.Parent != null)
                        heir.Parent.ChildChanged(heir);   //inform its parent
                }
            }
            else      //if no alias exists
            {
                if (_Item != null)
                    if (_Item.Drawable == this)  //check orignal
                        _Item.Remove();          //remove the item
                    else
                        if (_Item.Drawable != null && _Item.Drawable.Alias != null)   //this is an alias of another drawable
                            _Item.Drawable.Alias.Remove(this);   //remove reference
            }

            if (NullItem) Original = null;       //if this is an alias, remove from orignal's alias list

            if (_AnchoredItems != null)    //remove all linked items
            {
                List<IDrawable> links = new List<IDrawable>();
                links.AddRange(_AnchoredItems);
                foreach (IDrawable id in links)
                    id.Remove();

                AnchoredItems.Clear();
                AnchoredSource = null;
            }
        }
        /// <summary>
        /// what to do if this drawable is in close proximity of another drawable
        /// </summary>
        /// <param name="drawable">target drawable</param>
        /// <param name="moving">was the target in the process of moving</param>
        public virtual void HitBy(IDrawable drawable, bool moving) 
        {            
        }
        /// <summary>
        /// what to do if this drawable is close to another
        /// </summary>
        /// <param name="drawables">target drawable</param>
        /// <param name="moving">was the target moving</param>
        public virtual void HitBy(ICollection<IDrawable> drawables, bool moving)
        {
            drawables.Remove(this);
            foreach (IDrawable id in drawables)
            {
                HitBy(id,moving);
            }
        }

        protected IDrawable _Parent;
        /// <summary>
        /// Checks whether this drawable is a parent (or parent's parent's...parent) of 
        /// the given drawable 
        /// </summary>
        /// <param name="id"></param>
        public virtual bool IsParentOf(IDrawable child)
        {
            if (child == null) return false;
            IDrawable parent = child.Parent;
            while (parent != null)
            {
                if (parent == this) return true;
                parent = parent.Parent;
            }
            return false;
        }

        public abstract object Clone();

        protected string _sLabel;
        [CategoryAttribute("IDrawable Attributes"), Description("The name (non-unique) of this item inside the model")]
        public virtual string Text
        {
            get
            {
                if (string.IsNullOrEmpty(_sLabel))
                    return Name;
                return _sLabel;
            }
            set
            {
                _sLabel = value;
            }
        }

        protected Color _oLabelColor = Color.DarkBlue;

        [CategoryAttribute("IDrawable Attributes"), Description("The Color of the label.")]
        public virtual Color TextColor
        {
            get { return _oLabelColor; }
            set { _oLabelColor = value; }
        }

        protected Font _oLabelFont = SystemFonts.DefaultFont;

        [CategoryAttribute("IDrawable Attributes"), Description("The Font for the label of the Glyph.")]
        public virtual Font TextFont
        {
            get { return _oLabelFont; }
            set
            {
                //_bLabelChanged = true;
                _oLabelFont = value;
            }
        }

        private static string GetDrawableId(Athena.Objects.IDrawable drawable)
        {
            if (drawable.Item == null) return drawable.Name;
            Core.Module root = drawable.Item.Root as Core.Module;
            if (root == null) return drawable.Name;
            return root.Rename(drawable.Name, drawable.Item.Parent, root.AllItemsWithPrefix);
        }

        public string GlobalName
        {
            get
            {
                return GetDrawableId(this);
            }
        }

        [CategoryAttribute("Core Attributes"), Description("The unique ID of this item")]
        public virtual string Name
        {
            get
            {
                if (Item != null)
                    return Item.ID;
                else
                    return "";
            }
            set
            {
                if (Item != null)
                {
                    Item.ID = value;
                    //else
                     //   throw new Exception("Sorry, that ID already exists");
                        // MessageBox.Show("Sorry, that ID already exists");
                }
                _sLabel = value;
            }
        }
        [CategoryAttribute("Core Attributes"), Description("The Database ID of this item")]
        public virtual string DBID
        {
            get
            {
                if (Item != null)
                    return Item.DBID;
                else
                    return "";
            }
            set
            {
                if (Item != null)
                {
                    Item.DBID = value;
                }
            }
        }
        [CategoryAttribute("IDrawable Attributes"), Description("The drawable that contains this drawable")]
        public virtual IDrawable Parent
        {
            get
            {
                return _Parent;
            }
            set
            {
                if (value == _Parent) return;

                IDrawable oldParent = _Parent, newparent = value;
                _Parent = value;

                if (_Item != null && (((_Item.Parent == Program.Model.BackEndModel || _Item.Parent == null) && oldParent == null) //important: update item
                    || (_Item.Parent != null && oldParent != null && _Item.Parent == oldParent.Item)))
                    if (newparent != null)
                    {
                        _Item.Parent = newparent._Item;
                    }
                    else
                    {
                        _Item.Parent = Program.Model.BackEndModel;
                    }


                if (oldParent is ContainerGlyph && ((ContainerGlyph)oldParent).Drawables.Contains(this))
                {
                    ((ContainerGlyph)oldParent).RemoveItem(this);
                }
                if (newparent is ContainerGlyph && !((ContainerGlyph)newparent).Drawables.Contains(this))
                {
                    ((ContainerGlyph)newparent).AddItem(this,false);
                }
            }
        }
               
        /// <summary>
        /// what happens when one of the childred is changed
        /// </summary>
        /// <param name="child"></param>
        /// <returns></returns>
        public virtual bool ChildChanged(IDrawable child) { return true; }
        
        protected bool _Fixed = false;

        /// <summary>
        /// this drawable cannot be moved
        /// </summary>
        [CategoryAttribute("IDrawable Attributes"), Description("Whether or not this drawable can move")]        
        public virtual bool Immobile
        {
            get { return _Fixed; }
            set { _Fixed = value; }
        }

        public override string ToString()
        {
            if (Item != null)
            {
                return Item.ToString();
            }
            else
            {
                return "No Item";
            }
        }

        protected IDrawable _AnchoredSource;

        /// <summary>
        /// list of drawables that move along with this drawable
        /// </summary>
        [CategoryAttribute("IDrawable Attributes"), Description("the drawable that this one is anchored to")]        
        public virtual IDrawable AnchoredSource
        {
            get { return _AnchoredSource; }
            set
            {
                if (_AnchoredSource != null && _AnchoredSource.AnchoredItems != null)
                    _AnchoredSource.AnchoredItems.Remove(this);

                //if (_AnchoredItems != null && _AnchoredItems.Contains(value))
                //    _AnchoredItems.Remove(value);

                if (value != this)
                {
                    _AnchoredSource = value;

                    if (value != null && !value._AnchoredItems.Contains(this))
                    {
                        value._AnchoredItems.Add(this);
                    }
                }
                else
                    _AnchoredSource = null;
            }
        }

        protected List<IDrawable> _AnchoredItems = new List<IDrawable>();

        /// <summary>
        /// The set of drawables that are attached to this drawable
        /// </summary>
        [CategoryAttribute("IDrawable Attributes"), Description("all drawables anchored to this drawable")]        
        public virtual List<IDrawable> AnchoredItems
        {
            get 
            { 
                return _AnchoredItems; 
            }
            set 
            {
                if (value != null)
                    foreach (IDrawable id in value)
                    {
                        id.AnchoredSource = this;
                    }
                if (_AnchoredItems != null)
                    foreach (IDrawable id in _AnchoredItems)
                    {
                        id.AnchoredSource = null;
                    }
                _AnchoredItems = value;
            }
        }

        /// <summary>
        /// add decorators to SBW export
        /// </summary>
        /// <param name="oInfo"></param>
        /// <param name="oGroup"></param>
        public void AddDecoratorsToGroup(SBMLExtension.EmlRenderExtension.RenderInformation oInfo, SBMLExtension.EmlRenderExtension.Group oGroup)
        {
            foreach (IDrawable oDrawable in AnchoredItems)
            {
                if (oDrawable is Decorator)
                {
                    Decorator decorator = (Decorator)oDrawable;
                    RectangleF decRect = decorator.Bounds;
                    decRect.X -= Bounds.X;
                    decRect.Y -= Bounds.Y;

                    switch (decorator.Style)
                    {
                        case Decorator.DecoratorStyle.Ellipse:
                        //{
                        //}
                        //break;
                        case Decorator.DecoratorStyle.Circle:
                            {
                                SBMLExtension.EmlRenderExtension.Ellipses ellipse = new SBMLExtension.EmlRenderExtension.Ellipses();
                                ellipse.CX = (decRect.X + decRect.Width / 2f).ToString();
                                ellipse.CY = (decRect.Y + decRect.Height / 2f).ToString();
                                ellipse.RX = (decRect.Width / 2f).ToString();
                                ellipse.RY = (decRect.Height / 2f).ToString();
                                ellipse.Fill = oInfo.AddColorDefinition(Color.White);
                                ellipse.StrokeWidth = oGroup.StrokeWidth;
                                oGroup.Children.Add(ellipse);
                            }
                            break;
                        case Decorator.DecoratorStyle.Rectangle:
                            {
                                SBMLExtension.EmlRenderExtension.Rectangle rectangle = new SBMLExtension.EmlRenderExtension.Rectangle();
                                rectangle.X = (decRect.X).ToString();
                                rectangle.Y = (decRect.Y).ToString();
                                rectangle.Width = (decRect.Width).ToString();
                                rectangle.Height = (decRect.Height).ToString();
                                rectangle.Fill = oInfo.AddColorDefinition(Color.White);
                                rectangle.StrokeWidth = oGroup.StrokeWidth;
                                oGroup.Children.Add(rectangle);
                            }
                            break;
                        default:
                            break;
                    }

                    if (!string.IsNullOrEmpty(decorator.Text))
                    {
                        SBMLExtension.EmlRenderExtension.Text oText = new SBMLExtension.EmlRenderExtension.Text();
                        oText.FontFamily = decorator.TextFont.FontFamily.Name;
                        oText.FontSize = decorator.TextFont.SizeInPoints.ToString();
                        oText.Stroke = oInfo.AddColorDefinition(decorator.TextColor);
                        if (decorator.TextFont.Bold) oText.FontWeight = SBMLExtension.EmlRenderExtension.Text.FontWeiths.bold.ToString();
                        if (decorator.TextFont.Italic) oText.FontStyle = SBMLExtension.EmlRenderExtension.Text.FontStyles.italic.ToString();
                        oText.StrokeWidth = "1";
                        oText.Y = (decRect.Y + decRect.Height / 2f).ToString();
                        //oText.X = (decRect.X + decRect.Width / 2f - ((decRect.Width/decorator.Text.Length))).ToString();
                        oText.X = (decRect.X + decRect.Width / 2f).ToString();
                        oText.TheText = decorator.Text;
                        oText.TextAnchor = "middle";
                        oGroup.Children.Add(oText);
                    }
                }
            }
        }

        /// <summary>
        /// Returns a Style Representation for this glyph. If It is not supposed to be rendered, it will return null. 
        /// </summary>
        /// <param name="oInfo">The RenderInformation object to store Color, gradient and LineEnding definitions to.</param>
        /// <returns>A style representing this object or null.</returns>
        public virtual SBMLExtension.EmlRenderExtension.LocalStyle ToStyle(SBMLExtension.EmlRenderExtension.RenderInformation oInfo)
        {
            return null;
        }

        /// <summary>
        /// a boolean that indicated whether this drawable should be informed when it is hit by another drawable
        /// </summary>
        protected bool _RespondsToCollision = false;

        /// <summary>
        /// a boolean that indicated whether this drawable should be informed when it is hit by another drawable
        /// </summary>
        [CategoryAttribute("IDrawable Attributes"), Description("Whether or not to render this drawable responds when hit by another drawable")]        
        public virtual bool RespondsToCollision
        {
            get { return _RespondsToCollision; }
            set { _RespondsToCollision = value; }
        }

        /// <summary>
        /// write information about this drawable to an XML file
        /// </summary>
        /// <param name="reader"></param>
        public virtual void WriteXML(XmlWriter writer)
        {
            writer.WriteStartElement("IDrawable", "");

            writer.WriteStartElement("Label", "");
            writer.WriteString(Text);
            writer.WriteEndElement();

            writer.WriteStartElement("X", "");
            writer.WriteValue(Location.X);
            writer.WriteEndElement();
            
            writer.WriteStartElement("Y", "");
            writer.WriteValue(Location.Y);
            writer.WriteEndElement();

            writer.WriteStartElement("Width","");
            writer.WriteValue(Dimensions.Width);
            writer.WriteEndElement();

            writer.WriteStartElement("Height","");
            writer.WriteValue(Dimensions.Height);
            writer.WriteEndElement();

            writer.WriteStartElement("Fixed", "");
            writer.WriteValue(_Fixed);
            writer.WriteEndElement();

            writer.WriteStartElement("IsVisible", "");
            writer.WriteValue(_IsVisible);
            writer.WriteEndElement();

            writer.WriteStartElement("LabelH", "");
            writer.WriteValue((int)(_LabelH - HorizontalAlignment.Center));
            writer.WriteEndElement();

            writer.WriteStartElement("LabelV", "");
            writer.WriteValue((int)(_LabelV - VerticalAlignment.AboveTop));
            writer.WriteEndElement();

            writer.WriteStartElement("FillColorEnd", "");
            writer.WriteValue(_oFillColorEnd.Name);
            writer.WriteEndElement();

            writer.WriteStartElement("FillColorStart", "");
            writer.WriteValue(_oFillColorStart.Name);
            writer.WriteEndElement();

            writer.WriteStartElement("LabelColor", "");
            writer.WriteValue(_oLabelColor.Name);
            writer.WriteEndElement();

            writer.WriteStartElement("LabelFontName", "");
            writer.WriteValue(_oLabelFont.Name);
            writer.WriteEndElement();

            writer.WriteStartElement("LabelFontSize", "");
            writer.WriteValue(_oLabelFont.Size);
            writer.WriteEndElement();

            writer.WriteStartElement("RespondsToCollision", "");
            writer.WriteValue(_RespondsToCollision);
            writer.WriteEndElement();

            writer.WriteStartElement("ShowLabel", "");
            writer.WriteValue(_ShowLabel);
            writer.WriteEndElement();

            writer.WriteStartElement("ViewType", "");
            writer.WriteValue(View.ToString());
            writer.WriteEndElement();

            if (_AnchoredSource != null)
            {
                writer.WriteStartElement("AnchoredSource", "");
                writer.WriteValue(_AnchoredSource.Name);
                writer.WriteEndElement();
            }
            if (_AnchoredItems != null && _AnchoredItems.Count > 0)
            {
                writer.WriteStartElement("AnchoredItems", "");
                string[] names = new string[_AnchoredItems.Count];
                for (int i=0; i < _AnchoredItems.Count; ++i)
                {
                    names[i] = _AnchoredItems[i].Name;
                }
                writer.WriteValue(String.Join(",", names));
                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }

        /// <summary>
        /// read information about this drawable from an XML file
        /// </summary>
        /// <param name="reader"></param>
        public virtual bool ReadXML(XmlReader reader)
        {
            bool read = reader.Read();

            if (reader.NodeType == XmlNodeType.Element && reader.Name == "IDrawable")
            {
                float x = 0f, y = 0f, w = 10f, h = 10f;
                read = reader.Read();
                while (read)
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;

                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            case "Label":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    Text = reader.ReadContentAsString();
                                break;
                            case "X":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    x = reader.ReadContentAsFloat();
                                break;
                            case "Y":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    y = reader.ReadContentAsFloat();
                                break;
                            case "Width":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    w = reader.ReadContentAsFloat();
                                break;
                            case "Height":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    h = reader.ReadContentAsFloat();
                                break;
                            case "Fixed":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _Fixed = reader.ReadContentAsBoolean();
                                break;
                            case "IsVisible":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _IsVisible = reader.ReadContentAsBoolean();
                                break;
                            case "LabelH":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    if (reader.NodeType == XmlNodeType.Text)
                                        _LabelH = HorizontalAlignment.Center + reader.ReadContentAsInt();
                                }
                                break;
                            case "LabelV":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    if (reader.NodeType == XmlNodeType.Text)
                                        _LabelV = VerticalAlignment.AboveTop + reader.ReadContentAsInt();
                                }
                                break;
                            case "FillColorEnd":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _oFillColorEnd = Color.FromName(reader.ReadContentAsString());
                                break;
                            case "FillColorStart":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _oFillColorStart = Color.FromName(reader.ReadContentAsString());
                                break;
                            case "LabelColor":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _oLabelColor = Color.FromName(reader.ReadContentAsString());
                                break;
                            case "LabelFontName":
                                read = reader.Read();
                                float sz = 1f;
                                if (_oLabelFont == null) sz = _oLabelFont.Size;
                                if (reader.NodeType == XmlNodeType.Text)
                                    _oLabelFont = new Font(reader.ReadContentAsString(), sz);
                                break;
                            case "LabelFontSize":
                                read = reader.Read();
                                string name = SystemFonts.DefaultFont.Name;
                                if (_oLabelFont == null) name = _oLabelFont.Name;
                                if (reader.NodeType == XmlNodeType.Text)
                                    _oLabelFont = new Font(name, reader.ReadContentAsFloat());
                                break;
                            case "RespondsToCollision":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _RespondsToCollision = reader.ReadContentAsBoolean();
                                break;
                            case "ShowLabel":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _ShowLabel = reader.ReadContentAsBoolean();
                                break;
                            case "ViewType":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    string text = reader.ReadContentAsString();
                                    if (text == ViewType.Schematic.ToString())
                                        _ViewType = ViewType.Schematic;
                                    else
                                        _ViewType = ViewType.Block;
                                }
                                break;
                            case "AnchoredSource":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    string text = reader.ReadContentAsString();
                                    if (Parent is ContainerGlyph)
                                    {
                                        foreach (IDrawable id in ((ContainerGlyph)Parent).Drawables)
                                        {
                                            if (id.Name == text)
                                            {
                                                AnchoredSource = id;
                                                break;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        foreach (IDrawable id in Program.Model.Drawables)
                                        {
                                            if (id.Parent == null && id.Name == text)
                                            {
                                                AnchoredSource = id;
                                                break;
                                            }
                                        }
                                    }
                                }
                                break;
                            case "AnchoredItems":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    string textlist = reader.ReadContentAsString();
                                    string[] names = textlist.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                                    for (int i = 0; i < names.Length; ++i)
                                    {
                                        string text = names[i];
                                        if (Parent is ContainerGlyph)
                                        {
                                            foreach (IDrawable id in ((ContainerGlyph)Parent).Drawables)
                                            {
                                                if (id.Name == text)
                                                {
                                                    id.AnchoredSource = this;
                                                    break;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            foreach (IDrawable id in Program.Model.Drawables)
                                            {
                                                if (id.Parent == null && id.Name == text)
                                                {
                                                    id.AnchoredSource = this;
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                }
                                break;
                            default:
                                while (reader.NodeType != XmlNodeType.EndElement)
                                    read = reader.Read();
                                break;
                        }
                    }

                    if (!read || (reader.NodeType == XmlNodeType.EndElement && reader.Name == "IDrawable")) break;

                    if (reader.NodeType != XmlNodeType.Element)
                        read = reader.Read();
                }
                Location = new PointF(x, y);
                Dimensions = new SizeF(w, h);
                return true;
            }
            return false;
        }

        /// <summary>
        /// An optional function for loading the defaults of this drawable, if one exists 
        /// </summary>
        /// <param name="drawable">the drawable to use for the default view</param>
        public virtual void LoadDefaults(IDrawable drawable)
        {
        }
    }
}
